home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / Animator.java < prev    next >
Text File  |  1998-10-08  |  17KB  |  640 lines

  1. package symantec.itools.multimedia;
  2.  
  3. import java.awt.Graphics;
  4. import java.awt.MediaTracker;
  5. import java.awt.Canvas;
  6. import java.awt.Image;
  7. import java.awt.Dimension;
  8. import java.net.URL;
  9. import java.util.Vector;
  10. import java.util.Enumeration;
  11. import java.beans.PropertyVetoException;
  12. import java.beans.PropertyChangeListener;
  13. import java.beans.VetoableChangeListener;
  14. import java.beans.PropertyChangeEvent;
  15.  
  16. //    01/29/97    TWB    Integrated changes from Windows
  17. //    05/30/97    RKM    Changed symantec.beans references to java.beans
  18. //                    Moved AnimatorImage class into its own file as the compiler suggested
  19. //    05/31/97    RKM    Updated to support Java 1.1
  20. //                    Made properties bound & constrained
  21. //  07/15/97    CAR marked fields transient as needed
  22. //                  modified addImage to call the AnimatorImage constructor which takes a Component reference
  23. //  08/27/97    CAR setting repeatMode from false to true will re-start the animation
  24.  
  25. /**
  26.  * This is a simple animation component. <br>
  27.  * It creates an animation by displaying a series of images in sequence.
  28.  * The programmer can specify the delay between frames in milliseconds.
  29.  * The animation can loop for a specific
  30.  * number of iterations or can run forever.
  31.  *
  32.  * @version 1.0, Nov 26, 1996
  33.  * @author Symantec
  34.  */
  35.  
  36. public class Animator  extends Canvas implements Runnable, PropertyChangeListener
  37. {
  38.     /**
  39.      * Constructs a default Animator.  The animator defaults to a 500 millisecond delay, and loops forever.
  40.      */
  41.     public Animator()
  42.     {
  43.         delay         = 500;
  44.         numLoops      = 1;
  45.         forever       = true;
  46.         images        = new Vector();
  47.         currentImage  = null;
  48.         clearFrame    = false;
  49.         previewMode   = false;
  50.  
  51.         maxWidth      = 0;
  52.         maxHeight     = 0;
  53.  
  54.         vetos         = new symantec.itools.beans.VetoableChangeSupport(this);
  55.         changes       = new symantec.itools.beans.PropertyChangeSupport(this);
  56.  
  57.         changes.addPropertyChangeListener(this);
  58.     }
  59.  
  60.     //
  61.     // Properties
  62.     //
  63.  
  64.     /**
  65.      * Sets the delay between animation frames.
  66.      * @param i animation delay, in milliseconds
  67.      * @see #getDelay
  68.      * @exception PropertyVetoException
  69.      * if the specified property value is unacceptable
  70.      */
  71.     public void setDelay(int newDelay)
  72.         throws PropertyVetoException
  73.     {
  74.         if (delay != newDelay)
  75.         {
  76.             Integer oldDelayInt = new Integer(delay);
  77.             Integer newDelayInt = new Integer(newDelay);
  78.  
  79.             vetos.fireVetoableChange("Delay", oldDelayInt, newDelayInt);
  80.  
  81.             delay = newDelay;
  82.  
  83.             changes.firePropertyChange("Delay", oldDelayInt, newDelayInt);
  84.         }
  85.     }
  86.  
  87.     /**
  88.      * Returns the current delay between animation frames.
  89.      * @return current animation delay, in milliseconds
  90.      * @see #setDelay
  91.      */
  92.     public int getDelay()
  93.     {
  94.         return delay;
  95.     }
  96.  
  97.     /**
  98.      * Sets the number of loops to perform when displaying
  99.      * the animation set.
  100.      * @param i loop count
  101.      * @see #getNumLoops
  102.      * @exception PropertyVetoException
  103.      * if the specified property value is unacceptable
  104.      */
  105.     public void setNumLoops(int newNumLoops)
  106.         throws PropertyVetoException
  107.     {
  108.         if (numLoops != newNumLoops)
  109.         {
  110.             Integer oldNumLoopsInt = new Integer(numLoops);
  111.             Integer newNumLoopsInt = new Integer(newNumLoops);
  112.  
  113.             vetos.fireVetoableChange("NumLoops", oldNumLoopsInt, newNumLoopsInt);
  114.  
  115.             numLoops = newNumLoops;
  116.  
  117.             changes.firePropertyChange("NumLoops", oldNumLoopsInt, newNumLoopsInt);
  118.         }
  119.     }
  120.  
  121.     /**
  122.      * Returns the current animation set loop count.
  123.      * @return loop count
  124.      * @see #setNumLoops
  125.      */
  126.     public int getNumLoops()
  127.     {
  128.         return numLoops;
  129.     }
  130.  
  131.     /**
  132.      * Sets the repeat mode setting.
  133.      * @param b repeat mode, repeats if true
  134.      * @see #getRepeatMode
  135.      * @exception PropertyVetoException
  136.      * if the specified property value is unacceptable
  137.      */
  138.     public void setRepeatMode(boolean newRepeatMode)
  139.         throws PropertyVetoException
  140.     {
  141.         if (forever != newRepeatMode)
  142.         {
  143.             Boolean oldRepeatModeBool = new Boolean(forever);
  144.             Boolean newRepeatModeLoopsBool = new Boolean(newRepeatMode);
  145.  
  146.             vetos.fireVetoableChange("RepeatMode", oldRepeatModeBool, newRepeatModeLoopsBool);
  147.  
  148.             forever = newRepeatMode;
  149.  
  150.             changes.firePropertyChange("RepeatMode", oldRepeatModeBool, newRepeatModeLoopsBool);
  151.         }
  152.     }
  153.  
  154.     /**
  155.      * @deprecated
  156.      * @see #isRepeatMode
  157.      */
  158.     public boolean getRepeatMode()
  159.     {
  160.         return isRepeatMode();
  161.     }
  162.  
  163.     /**
  164.      * Returns the current repeat mode setting.
  165.      * @return current repeat mode setting, true if repeat forever
  166.      * @see #setRepeatMode
  167.      */
  168.     public boolean isRepeatMode()
  169.     {
  170.         return forever;
  171.     }
  172.  
  173.     /**
  174.      * Sets the image list.  Images in this list are displayed in
  175.      * sequence to form the animation.
  176.      * @param list array of image URLs
  177.      * @see #getImageList
  178.      * @exception PropertyVetoException
  179.      * if the specified property value is unacceptable
  180.      */
  181.     public synchronized void setImageList(URL[] newImageList)
  182.         throws PropertyVetoException
  183.     {
  184.         //???RKM??? I'm not comparing the two lists here, should I?
  185.  
  186.         boolean wasAnimating = (displayThread != null);
  187.  
  188.         if (wasAnimating)
  189.             stopAnimation();
  190.  
  191.         URL[] oldImageList = getImageList();
  192.  
  193.         vetos.fireVetoableChange("ImageList", oldImageList, newImageList);
  194.  
  195.         currentImage = null;
  196.  
  197.         images = new Vector();
  198.         for (int i = 0; i < newImageList.length; ++i)
  199.             addImage(newImageList[i]);
  200.  
  201.         changes.firePropertyChange("ImageList", oldImageList, newImageList);
  202.  
  203.         if (wasAnimating || previewMode || !java.beans.Beans.isDesignTime())
  204.         {
  205.             startAnimation();
  206.         }
  207.         else if (newImageList.length > 0)
  208.         {
  209.             currentImage = ((AnimatorImage)images.elementAt(0)).image;
  210.         }
  211.  
  212.         repaint();
  213.     }
  214.  
  215.     /**
  216.      * Returns the image list.
  217.      * @return URL list of images
  218.      * @see #setImageList
  219.      */
  220.     public synchronized URL[] getImageList()
  221.     {
  222.         URL[] list = new URL[images.size()];
  223.  
  224.         int i = 0;
  225.         Enumeration enum = images.elements();
  226.         while (enum.hasMoreElements())
  227.         {
  228.             list[i++] = ((AnimatorImage)enum.nextElement()).url;
  229.         }
  230.  
  231.         return list;
  232.     }
  233.  
  234.     /**
  235.      * Sets whether or not the animation frame area is cleared
  236.      * between each frame.
  237.      * @param newClearFrame if true, the frame area is cleared between each
  238.      * animation frame; if false, the frame area is not cleared.
  239.      * @see #getClearFrame
  240.      * @exception PropertyVetoException
  241.      * if the specified property value is unacceptable
  242.      */
  243.     public void setClearFrame(boolean newClearFrame)
  244.         throws PropertyVetoException
  245.     {
  246.         if (clearFrame = newClearFrame)
  247.         {
  248.             Boolean oldClearFrameBool = new Boolean(clearFrame);
  249.             Boolean newClearFrameBool = new Boolean(newClearFrame);
  250.  
  251.             vetos.fireVetoableChange("ClearFrame", oldClearFrameBool, newClearFrameBool);
  252.  
  253.             clearFrame = newClearFrame;
  254.  
  255.             changes.firePropertyChange("ClearFrame", oldClearFrameBool, newClearFrameBool);
  256.         }
  257.     }
  258.  
  259.     /**
  260.      * @deprecated
  261.      * @see #isClearFrame
  262.      */
  263.     public boolean getClearFrame()
  264.     {
  265.         return isClearFrame();
  266.     }
  267.  
  268.     /**
  269.      * Gets the current clear frame setting.
  270.      * @return boolean - if true, the frame area is cleared
  271.      * between each animation frame; if false, the frame area
  272.      * is not cleared.
  273.      * @see #setClearFrame
  274.      */
  275.     public boolean isClearFrame()
  276.     {
  277.         return clearFrame;
  278.     }
  279.  
  280.     /**
  281.      * Sets the preview mode flag.  This flag is used by Visual Cafe to
  282.      * determine if this component should be run during design time.
  283.      * @param newPreviewMode new preview mode
  284.      * @see #getPreviewMode
  285.      */
  286.     public void setPreviewMode(boolean newPreviewMode)
  287.     {
  288.         if (previewMode != newPreviewMode)
  289.         {
  290.             if (java.beans.Beans.isDesignTime())
  291.             {
  292.                 previewMode = newPreviewMode;
  293.  
  294.                 if (previewMode && (images.size() > 0))
  295.                     startAnimation();
  296.                 else
  297.                     stopAnimation();
  298.             }
  299.         }
  300.     }
  301.  
  302.     /**
  303.      * @deprecated
  304.      * @see #setPreviewMode
  305.      */
  306.     public boolean getPreviewMode()
  307.     {
  308.         return isPreviewMode();
  309.     }
  310.  
  311.     /**
  312.      * Gets the preview mode flag. This flag is used by Visual Cafe to
  313.      * determine if this component should be run during design time.
  314.      * @see #setPreviewMode
  315.      */
  316.     public boolean isPreviewMode()
  317.     {
  318.         return previewMode;
  319.     }
  320.  
  321.     //
  322.     // Methods
  323.     //
  324.  
  325.     /**
  326.      * Adds an image to the animation set.
  327.      * @param url URL of the image to add
  328.      */
  329.     public synchronized void addImage(URL url)
  330.     {
  331.         Image image = getToolkit().getImage(url);
  332.  
  333.         boolean loadWait = images.size() == 0 || previewMode || !java.beans.Beans.isDesignTime();
  334.  
  335.         if (loadWait)
  336.         {
  337.             imageLoadWait(image);
  338.         }
  339.  
  340.         images.addElement(new AnimatorImage(url, image, loadWait, this));
  341.     }
  342.  
  343.     /**
  344.      * Starts the animation.
  345.      * @see #stopAnimation
  346.      */
  347.     public void startAnimation()
  348.     {
  349.         if (displayThread == null)
  350.         {
  351.             displayThread = new Thread(this);
  352.             displayThread.start();
  353.         }
  354.         else
  355.         {
  356.             try {
  357.                 displayThread.join();
  358.             } catch (InterruptedException e) {}
  359.             displayThread = new Thread(this);
  360.             displayThread.start();
  361.         }
  362.     }
  363.  
  364.     /**
  365.      * Stops the animation.
  366.      * @see #startAnimation
  367.      */
  368.     public void stopAnimation()
  369.     {
  370.         if (displayThread != null)
  371.         {
  372.             displayThread.stop();
  373.             displayThread = null;
  374.         }
  375.     }
  376.  
  377.     /**
  378.      * Body of Animation Thread.  This method is called by the Java Virtual Machine
  379.      * in response to a call to the start method of this object.
  380.      */
  381.  
  382.     public synchronized void run()
  383.     {
  384.         for (int i = 0; i < numLoops || forever; ++i)
  385.         {
  386.             if (images.size() == 0)
  387.             {
  388.                 try
  389.                 {
  390.                     wait(delay);
  391.                 }
  392.                 catch(InterruptedException e)
  393.                 {
  394.                 }
  395.  
  396.             }
  397.             else
  398.             {
  399.  
  400.                 for (int j = 0; j < images.size(); ++j)
  401.                 {
  402.                    synchronized(this)
  403.                    {
  404.                         try
  405.                         {
  406.                             wait(delay);
  407.                         }
  408.                         catch(InterruptedException e)
  409.                         {
  410.                         }
  411.  
  412.                         AnimatorImage ai = (AnimatorImage)images.elementAt(j);
  413.  
  414.                         if (!ai.loaded)
  415.                         {
  416.                             imageLoadWait(ai.image);
  417.                             images.setElementAt(new AnimatorImage(ai.url, ai.image, true), j);
  418.                         }
  419.  
  420.                         currentImage = ai.image;
  421.                     }
  422.  
  423.                     repaint();
  424.                 }
  425.             }
  426.         }
  427.     }
  428.  
  429.     /**
  430.      * Paints this component using the given graphics context.
  431.      * This is a standard Java AWT method which typically gets called
  432.      * by the AWT to handle painting this component. It paints this component
  433.      * using the given graphics context. The graphics context clipping region
  434.      * is set to the bounding rectangle of this component and its <0,0>
  435.      * coordinate is this component's top-left corner.
  436.      *
  437.      * @param g the graphics context used for painting
  438.      * @see java.awt.Component#repaint
  439.      * @see #update
  440.      */
  441.     public synchronized void paint(Graphics g)
  442.     {
  443.         if (currentImage != null)
  444.             g.drawImage(currentImage, 0, 0, this);
  445.     }
  446.  
  447.     /**
  448.      * Handles redrawing of this component on the screen.
  449.      * This is a standard Java AWT method which gets called by the Java
  450.      * AWT (repaint()) to handle repainting this component on the screen.
  451.      * The graphics context clipping region is set to the bounding rectangle
  452.      * of this component and its <0,0> coordinate is this component's
  453.      * top-left corner.
  454.      * Typically this method paints the background color to clear the
  455.      * component's drawing space, sets graphics context to be the foreground
  456.      * color, and then calls paint() to draw the component.
  457.      *
  458.      * It is overridden here to make clearing the background before painting
  459.      * optional. If the clearFrame flag is true the background will be erased
  460.      * before painting begins.
  461.      *
  462.      * @param g the graphics context
  463.      * @see java.awt.Component#repaint
  464.      * @see #paint
  465.      */
  466.     public void update(Graphics g)
  467.     {
  468.         if (clearFrame)
  469.             super.update(g);
  470.         else
  471.             paint(g);
  472.     }
  473.  
  474.     /**
  475.      * Returns the recommended dimensions to properly display this component.
  476.      * This is a standard Java AWT method which gets called to determine
  477.      * the recommended size of this component.
  478.      *
  479.      * @return  If no images have been loaded, a dimension of 10 by 10 is returned.
  480.      *          If one or more images have been loaded, the largest height and the
  481.      *          largest width of any image is returned.
  482.      *
  483.      * @see #minimumSize
  484.      */
  485.     public Dimension preferredSize()
  486.     {
  487.         if (images == null || images.size() == 0)
  488.             return new Dimension(10, 10);
  489.  
  490.         return new Dimension(maxWidth, maxHeight);
  491.     }
  492.  
  493.     /**
  494.      * Returns the minimum dimensions to properly display this component.
  495.      * This is a standard Java AWT method which gets called to determine
  496.      * the minimum size of this component.
  497.      *
  498.      * @return If no images have been loaded, a dimension of 10 by 10 is returned.
  499.      *         If one or more images have been loaded, the largest height and the
  500.      *         largest width of any image is returned.
  501.      *
  502.      * @see #preferredSize
  503.      */
  504.     public Dimension minimumSize()
  505.     {
  506.         return preferredSize();
  507.     }
  508.  
  509.     /**
  510.      * A method of the PropertyChangeListener interface.
  511.      * This method is called when a property of this component changes.
  512.      * It starts the animation when the Repeat Mode property is set.
  513.      * @param evt the event
  514.      * @see java.awt.PropertyChangeListener
  515.      */
  516.     public void propertyChange(PropertyChangeEvent evt)
  517.     {
  518.         String property = evt.getPropertyName();
  519.  
  520.         if(property != null) {
  521.             if(property.equalsIgnoreCase("RepeatMode") && previewMode ) {
  522.                 startAnimation();
  523.             }
  524.         }
  525.  
  526.     }
  527.  
  528.     /**
  529.      * Adds a listener for all event changes.
  530.      * @param PropertyChangeListener listener the listener to add.
  531.      * @see #removePropertyChangeListener
  532.      */
  533.     public void addPropertyChangeListener(PropertyChangeListener listener)
  534.     {
  535.         changes.addPropertyChangeListener(listener);
  536.     }
  537.  
  538.     /**
  539.      * Removes a listener for all event changes.
  540.      * @param PropertyChangeListener listener the listener to remove.
  541.      * @see #addPropertyChangeListener
  542.      */
  543.     public void removePropertyChangeListener(PropertyChangeListener listener)
  544.     {
  545.         changes.removePropertyChangeListener(listener);
  546.     }
  547.  
  548.     /**
  549.      * Adds a vetoable listener for all event changes.
  550.      * @param VetoableChangeListener listener the listener to add.
  551.      * @see #removeVetoableChangeListener
  552.      */
  553.     public void addVetoableChangeListener(VetoableChangeListener listener)
  554.     {
  555.         vetos.addVetoableChangeListener(listener);
  556.     }
  557.  
  558.     /**
  559.      * Removes a vetoable listener for all event changes.
  560.      * @param VetoableChangeListener listener the listener to remove.
  561.      * @see #addVetoableChangeListener
  562.      */
  563.     public void removeVetoableChangeListener(VetoableChangeListener listener)
  564.     {
  565.         vetos.removeVetoableChangeListener(listener);
  566.     }
  567.  
  568.     void imageLoadWait(Image image)
  569.     {
  570.         MediaTracker tracker = new MediaTracker(this);
  571.  
  572.         tracker.addImage(image, 0);
  573.  
  574.         try
  575.         {
  576.             tracker.waitForAll();
  577.         }
  578.         catch(InterruptedException e)
  579.         {
  580.         }
  581.  
  582.         int size;
  583.  
  584.         if ((size = image.getWidth(this)) > maxWidth)
  585.             maxWidth = size;
  586.  
  587.         if ((size = image.getHeight(this)) > maxHeight)
  588.             maxHeight = size;
  589.     }
  590.  
  591.     /**
  592.      * Delay time between images, in milliseconds.
  593.      */
  594.     protected int delay;
  595.  
  596.     /**
  597.      * Number of times to show the animation sequence.
  598.      */
  599.     protected int numLoops;
  600.  
  601.     /**
  602.      * Run animation forever. If false, use numLoops.
  603.      */
  604.     protected boolean forever;
  605.  
  606.     /**
  607.      * Images to be displayed.
  608.      */
  609.     protected Vector images;
  610.  
  611.     /**
  612.      * Image currently being shown.
  613.      */
  614.     transient protected Image currentImage;
  615.  
  616.     /**
  617.      * Thread which runs the animation.
  618.      */
  619.     transient protected Thread displayThread;
  620.  
  621.     /**
  622.      * Dimension of largest image in sequence.
  623.      */
  624.     protected int maxWidth, maxHeight;
  625.  
  626.     /**
  627.      * Clear frame between each image.
  628.      */
  629.     protected boolean clearFrame;
  630.  
  631.     /**
  632.      * Preview this component at design time.
  633.      */
  634.     protected boolean previewMode;
  635.  
  636.     // Private members
  637.     private symantec.itools.beans.VetoableChangeSupport vetos;
  638.     private symantec.itools.beans.PropertyChangeSupport changes;
  639. }
  640.